/**
 * 性能监控和错误追踪工具
 */

export interface PerformanceConfig {
  enableTracking: boolean
  sampleRate: number
  apiEndpoint?: string
  bufferSize: number
  flushInterval: number
}

export interface PerformanceEntry {
  type: 'navigation' | 'resource' | 'measure' | 'mark' | 'error' | 'custom'
  name: string
  value: number
  timestamp: number
  url?: string
  userAgent?: string
  metadata?: Record<string, any>
}

export interface ErrorEntry {
  type: 'javascript' | 'resource' | 'promise' | 'network'
  message: string
  filename?: string
  lineno?: number
  colno?: number
  error?: Error
  timestamp: number
  url?: string
  userAgent?: string
  stack?: string
}

class PerformanceTracker {
  private config: PerformanceConfig
  private performanceBuffer: PerformanceEntry[] = []
  private errorBuffer: ErrorEntry[] = []
  private flushTimer: NodeJS.Timeout | null = null
  private observer: PerformanceObserver | null = null

  constructor(config: Partial<PerformanceConfig> = {}) {
    this.config = {
      enableTracking: true,
      sampleRate: 1.0,
      bufferSize: 100,
      flushInterval: 30000, // 30秒
      ...config
    }

    if (this.config.enableTracking) {
      this.init()
    }
  }

  public mark(name: string, metadata?: Record<string, any>) {
    performance.mark(name)

    const entry: PerformanceEntry = {
      type: 'mark',
      name,
      value: performance.now(),
      timestamp: Date.now(),
      url: location.href,
      userAgent: navigator.userAgent,
      metadata
    }

    this.addPerformanceEntry(entry)
  }

  public measure(name: string, startMark: string, endMark?: string, metadata?: Record<string, any>) {
    performance.measure(name, startMark, endMark)

    const measureEntries = performance.getEntriesByName(name, 'measure')
    const latestMeasure = measureEntries[measureEntries.length - 1]

    if (latestMeasure) {
      const entry: PerformanceEntry = {
        type: 'measure',
        name,
        value: latestMeasure.duration,
        timestamp: Date.now(),
        url: location.href,
        userAgent: navigator.userAgent,
        metadata
      }

      this.addPerformanceEntry(entry)
    }
  }

  public trackCustomMetric(name: string, value: number, metadata?: Record<string, any>) {
    const entry: PerformanceEntry = {
      type: 'custom',
      name,
      value,
      timestamp: Date.now(),
      url: location.href,
      userAgent: navigator.userAgent,
      metadata
    }

    this.addPerformanceEntry(entry)
  }

  public getVitalMetrics() {
    const navigation = performance.getEntriesByType('navigation')[0] as PerformanceNavigationTiming
    const paint = performance.getEntriesByType('paint')

    const fcp = paint.find(entry => entry.name === 'first-contentful-paint')
    const lcp = performance.getEntriesByType('largest-contentful-paint').pop()
    const fid = performance.getEntriesByType('first-input').pop()
    const cls = performance.getEntriesByType('layout-shift')
      .reduce((sum, entry) => sum + (entry as any).value, 0)

    return {
      // 页面加载时间
      ttfb: navigation ? navigation.responseStart - navigation.requestStart : 0,
      fcp: fcp ? fcp.startTime : 0,
      lcp: lcp ? lcp.startTime : 0,
      domContentLoaded: navigation ? navigation.domContentLoadedEventEnd - navigation.navigationStart : 0,
      loadComplete: navigation ? navigation.loadEventEnd - navigation.navigationStart : 0,

      // 交互性指标
      fid: fid ? fid.processingStart - fid.startTime : 0,
      cls: cls,

      // 资源统计
      resourceCount: performance.getEntriesByType('resource').length,
      totalTransferSize: performance.getEntriesByType('resource')
        .reduce((sum, entry) => sum + ((entry as PerformanceResourceTiming).transferSize || 0), 0)
    }
  }

  public destroy() {
    if (this.observer) {
      this.observer.disconnect()
    }

    if (this.flushTimer) {
      clearInterval(this.flushTimer)
    }

    this.flush()
  }

  private init() {
    this.setupPerformanceObserver()
    this.setupErrorHandlers()
    this.startFlushTimer()
    this.trackPageLoad()
  }

  private setupPerformanceObserver() {
    if (!('PerformanceObserver' in window)) {
      console.warn('PerformanceObserver not supported')
      return
    }

    try {
      this.observer = new PerformanceObserver((list) => {
        const entries = list.getEntries()

        entries.forEach(entry => {
          if (Math.random() > this.config.sampleRate) {
            return
          }

          const perfEntry: PerformanceEntry = {
            type: this.getEntryType(entry.entryType as any),
            name: entry.name,
            value: entry.duration || entry.startTime,
            timestamp: Date.now(),
            url: location.href,
            userAgent: navigator.userAgent,
            metadata: this.extractMetadata(entry)
          }

          this.addPerformanceEntry(perfEntry)
        })
      })

      // 观察所有性能条目类型
      this.observer.observe({
        entryTypes: [
          'navigation',
          'resource',
          'paint',
          'largest-contentful-paint',
          'first-input',
          'layout-shift'
        ]
      })
    } catch (error) {
      console.warn('Failed to setup PerformanceObserver:', error)
    }
  }

  private setupErrorHandlers() {
    // JavaScript错误
    window.addEventListener('error', (event) => {
      const errorEntry: ErrorEntry = {
        type: 'javascript',
        message: event.message,
        filename: event.filename,
        lineno: event.lineno,
        colno: event.colno,
        error: event.error,
        timestamp: Date.now(),
        url: location.href,
        userAgent: navigator.userAgent,
        stack: event.error?.stack
      }

      this.addErrorEntry(errorEntry)
    })

    // Promise拒绝错误
    window.addEventListener('unhandledrejection', (event) => {
      const errorEntry: ErrorEntry = {
        type: 'promise',
        message: String(event.reason),
        timestamp: Date.now(),
        url: location.href,
        userAgent: navigator.userAgent,
        stack: event.reason?.stack
      }

      this.addErrorEntry(errorEntry)
    })

    // 资源加载错误
    window.addEventListener('error', (event) => {
      if (event.target !== window) {
        const target = event.target as HTMLElement
        const errorEntry: ErrorEntry = {
          type: 'resource',
          message: `Failed to load resource: ${target.tagName}`,
          filename: (target as any).src || (target as any).href,
          timestamp: Date.now(),
          url: location.href,
          userAgent: navigator.userAgent
        }

        this.addErrorEntry(errorEntry)
      }
    }, true)
  }

  private trackPageLoad() {
    // 页面可见性变化
    document.addEventListener('visibilitychange', () => {
      const entry: PerformanceEntry = {
        type: 'custom',
        name: 'page-visibility',
        value: document.hidden ? 0 : 1,
        timestamp: Date.now(),
        url: location.href,
        userAgent: navigator.userAgent,
        metadata: {
          visibilityState: document.visibilityState
        }
      }

      this.addPerformanceEntry(entry)
    })

    // 页面卸载
    window.addEventListener('beforeunload', () => {
      this.flush()
    })
  }

  private startFlushTimer() {
    this.flushTimer = setInterval(() => {
      this.flush()
    }, this.config.flushInterval)
  }

  private getEntryType(entryType: string): PerformanceEntry['type'] {
    switch (entryType) {
      case 'navigation': return 'navigation'
      case 'resource': return 'resource'
      case 'measure': return 'measure'
      case 'mark': return 'mark'
      default: return 'custom'
    }
  }

  private extractMetadata(entry: PerformanceEntry): Record<string, any> {
    const metadata: Record<string, any> = {}

    // 导航时间详情
    if (entry.entryType === 'navigation') {
      const navEntry = entry as PerformanceNavigationTiming
      metadata.connectTime = navEntry.connectEnd - navEntry.connectStart
      metadata.domContentLoaded = navEntry.domContentLoadedEventEnd - navEntry.domContentLoadedEventStart
      metadata.loadComplete = navEntry.loadEventEnd - navEntry.loadEventStart
      metadata.redirectCount = navEntry.redirectCount
      metadata.transferSize = navEntry.transferSize
      metadata.encodedBodySize = navEntry.encodedBodySize
      metadata.decodedBodySize = navEntry.decodedBodySize
    }

    // 资源加载详情
    if (entry.entryType === 'resource') {
      const resourceEntry = entry as PerformanceResourceTiming
      metadata.transferSize = resourceEntry.transferSize
      metadata.encodedBodySize = resourceEntry.encodedBodySize
      metadata.decodedBodySize = resourceEntry.decodedBodySize
      metadata.initiatorType = resourceEntry.initiatorType
    }

    // 核心Web指标
    if (entry.name === 'largest-contentful-paint') {
      metadata.element = (entry as any).element?.tagName
    }

    if (entry.entryType === 'layout-shift') {
      metadata.value = (entry as any).value
      metadata.hadRecentInput = (entry as any).hadRecentInput
    }

    return metadata
  }

  private addPerformanceEntry(entry: PerformanceEntry) {
    this.performanceBuffer.push(entry)

    if (this.performanceBuffer.length >= this.config.bufferSize) {
      this.flush()
    }
  }

  private addErrorEntry(entry: ErrorEntry) {
    this.errorBuffer.push(entry)

    if (this.errorBuffer.length >= this.config.bufferSize) {
      this.flush()
    }
  }

  private async flush() {
    if (this.performanceBuffer.length === 0 && this.errorBuffer.length === 0) {
      return
    }

    const payload = {
      performance: [...this.performanceBuffer],
      errors: [...this.errorBuffer],
      session: {
        timestamp: Date.now(),
        url: location.href,
        userAgent: navigator.userAgent,
        viewport: {
          width: window.innerWidth,
          height: window.innerHeight
        },
        connection: this.getConnectionInfo()
      }
    }

    // 清空缓冲区
    this.performanceBuffer = []
    this.errorBuffer = []

    if (this.config.apiEndpoint) {
      try {
        await fetch(this.config.apiEndpoint, {
          method: 'POST',
          headers: {
            'Content-Type': 'application/json'
          },
          body: JSON.stringify(payload),
          keepalive: true
        })
      } catch (error) {
        console.warn('Failed to send performance data:', error)
      }
    } else {
      // 开发环境下打印到控制台
      console.group('Performance & Error Data')
      console.log('Performance entries:', payload.performance)
      console.log('Error entries:', payload.errors)
      console.log('Session info:', payload.session)
      console.groupEnd()
    }
  }

  private getConnectionInfo() {
    // @ts-ignore
    const connection = navigator.connection || navigator.mozConnection || navigator.webkitConnection

    if (connection) {
      return {
        effectiveType: connection.effectiveType,
        downlink: connection.downlink,
        rtt: connection.rtt,
        saveData: connection.saveData
      }
    }

    return null
  }
}

// 全局性能追踪器实例
let tracker: PerformanceTracker | null = null

export function initPerformanceTracking(config?: Partial<PerformanceConfig>) {
  if (!tracker) {
    tracker = new PerformanceTracker(config)
  }
  return tracker
}

export function getPerformanceTracker(): PerformanceTracker | null {
  return tracker
}

// Vue插件形式
export function createPerformancePlugin(config?: Partial<PerformanceConfig>) {
  return {
    install(app: any) {
      const tracker = initPerformanceTracking(config)

      app.config.globalProperties.$perf = tracker
      app.provide('performance', tracker)

      // Vue路由切换性能监控
      if (app.config.globalProperties.$router) {
        const router = app.config.globalProperties.$router

        router.beforeEach((to: any) => {
          tracker?.mark(`route-start-${to.name}`)
        })

        router.afterEach((to: any) => {
          tracker?.mark(`route-end-${to.name}`)
          tracker?.measure(
            `route-${to.name}`,
            `route-start-${to.name}`,
            `route-end-${to.name}`,
            { routePath: to.path, routeName: to.name }
          )
        })
      }
    }
  }
}

// Web Vitals兼容性检测
export function checkWebVitalsSupport() {
  return {
    performanceObserver: 'PerformanceObserver' in window,
    navigationTiming: 'PerformanceNavigationTiming' in window,
    resourceTiming: 'PerformanceResourceTiming' in window,
    paintTiming: PerformanceObserver.supportedEntryTypes?.includes('paint') || false,
    largestContentfulPaint: PerformanceObserver.supportedEntryTypes?.includes('largest-contentful-paint') || false,
    firstInputDelay: PerformanceObserver.supportedEntryTypes?.includes('first-input') || false,
    cumulativeLayoutShift: PerformanceObserver.supportedEntryTypes?.includes('layout-shift') || false
  }
}
